﻿using Microsoft.ExtendedReflection.Collections;
using Microsoft.ExtendedReflection.ComponentModel;
using Microsoft.ExtendedReflection.Remote;
using Microsoft.ExtendedReflection.Utilities;
using Microsoft.ExtendedReflection.Utilities.Safe;
using Microsoft.VisualStudio.TestTools.TestAdapter;
using System;
using System.Collections.Generic;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;

namespace MolesVS2012.Host.Agent
{
    [Serializable]
    internal sealed class MolesHostAdapterManager : ComponentBase, IService
    {
        private readonly SafeDictionary<string, HostTestAdapterDriver> hostAdapterDrivers = new SafeDictionary<string, HostTestAdapterDriver>((IEqualityComparer<string>)StringComparer.InvariantCultureIgnoreCase);
        private string _serverChannel;
        private IChannel _channel;

        public string ServerChannel
        {
            get
            {
                return this._serverChannel;
            }
        }

        public IEnumerable<ITestAdapter> HostAdapters
        {
            get
            {
                HostTestAdapterDriver[] hostAdapterDrivers = this.GetAllHostAdapterDrivers();
                int index = 0;
            label_8:
                if (index < hostAdapterDrivers.Length)
                {
                    HostTestAdapterDriver hostAdapterDriver = hostAdapterDrivers[index];
                    bool isAlive;
                    try
                    {
                        isAlive = hostAdapterDriver.IsAlive;
                    }
                    catch (RemotingException ex)
                    {
                        this.Log.LogWarningFromException((Exception)ex, "Missing Wiki Topic", "Host", "communication with the remote test host was lost");
                        isAlive = false;
                    }
                    catch (Exception ex)
                    {
                        this.Log.LogWarningFromException(ex, "Missing Wiki Topic", "Host", "unexpected error in Pex Host type");
                        isAlive = false;
                    }
                    if (isAlive)
                        yield return (ITestAdapter)hostAdapterDriver;
                    ++index;
                    goto label_8;
                }
            }
        }

        protected override void Initialize()
        {
            this._serverChannel = SafeString.Format("MolesVsHost{0}", (object)ProcessHelper.CurrentProcess.Id);
            if (!RemoteServer.TryRegisterServer(this._serverChannel, out this._channel))
            {
                this.Log.LogError("Missing Wiki Topic", "Agent", "failed to register server over channel {0}", new object[1]
        {
          (object) this._serverChannel
        });
                this._channel = (IChannel)null;
                this._serverChannel = (string)null;
            }
            else
                this.Log.LogMessage("Agent", "channel {0} started", new object[1]
        {
          (object) this._serverChannel
        });
        }

        public bool TryGetOrCreateHostAdapter(string assembly, out ITestAdapter hostAdapter)
        {
            if (this._channel == null)
            {
                hostAdapter = (ITestAdapter)null;
                return false;
            }
            else
            {
                HostTestAdapterDriver testAdapterDriver;
                lock (this.hostAdapterDrivers)
                {
                    if (!this.hostAdapterDrivers.TryGetValue(assembly, out testAdapterDriver))
                        this.hostAdapterDrivers[assembly] = testAdapterDriver = new HostTestAdapterDriver((IComponent)this, assembly);
                }
                hostAdapter = (ITestAdapter)testAdapterDriver;
                return true;
            }
        }

        private HostTestAdapterDriver[] GetAllHostAdapterDrivers()
        {
            lock (this.hostAdapterDrivers)
                return Enumerable.ToArray<HostTestAdapterDriver>((IEnumerable<HostTestAdapterDriver>)this.hostAdapterDrivers.Values);
        }

        public void CleanUpHostAdapters()
        {
            foreach (ITestAdapter testAdapter in this.HostAdapters)
            {
                try
                {
                    testAdapter.Cleanup();
                }
                catch (RemotingException ex)
                {
                    this.Log.LogWarningFromException((Exception)ex, "Missing Wiki Topic", "Host", "communication with the remote test host was lost");
                }
                catch (Exception ex)
                {
                    this.Log.LogWarningFromException(ex, "Missing Wiki Topic", "Host", "unexpected error in Pex Host type");
                }
            }
        }

        public void ForAllHostAdapters(Action<ITestAdapter> action)
        {
            foreach (ITestAdapter testAdapter in this.HostAdapters)
            {
                try
                {
                    action(testAdapter);
                }
                catch (RemotingException ex)
                {
                    this.Log.LogErrorFromException((Exception)ex, "Missing Wiki Topic", "Host", "communication with the remote test host was lost");
                }
                catch (Exception ex)
                {
                    this.Log.LogErrorFromException(ex, "Missing Wiki Topic", "Host", "unexpected error in Pex Host type");
                }
            }
        }

        public void CloseHostAdapters()
        {
            HostTestAdapterDriver[] testAdapterDriverArray;
            lock (this.hostAdapterDrivers)
            {
                testAdapterDriverArray = Enumerable.ToArray<HostTestAdapterDriver>((IEnumerable<HostTestAdapterDriver>)this.hostAdapterDrivers.Values);
                this.hostAdapterDrivers.Clear();
            }
            foreach (HostTestAdapterDriver testAdapterDriver in testAdapterDriverArray)
            {
                try
                {
                    testAdapterDriver.Dispose();
                }
                catch
                {
                }
            }
            IChannel channel = this._channel;
            if (channel == null)
                return;
            this._channel = (IChannel)null;
            try
            {
                RemoteServer.UnregisterServer(channel);
            }
            catch (RemotingException ex)
            {
            }
        }

        public override void Dispose()
        {
            this.CloseHostAdapters();
            base.Dispose();
        }
    }
}
